//
// Project werewolf: repo.cpp
// Created by alfielin on 2021/12/05.
//

#pragma once

#include "sqlite_orm.cpp"
#include "constants.cpp"
#include "util/myoptional.cpp"
#include <algorithm>

namespace nz {
namespace picit {
namespace api {
using namespace sqlite_orm;

struct PicitEntity {
    int _id{};
    std::string vpath;
    std::string abspath;
    int version{};
    std::string sha1;
    bool removed{};
    std::string create_time;
    std::string update_time;

    bool operator<(const PicitEntity &b) const {
        return version < b.version;
    }
};

class PicitRepository {

ENABLE_SINGLETON(PicitRepository);

private:

    PicitRepository() = default;

    auto &repo() {
        static auto storage =
                make_storage(SQLITE_DBFILE,
                             make_table("picit",
                                        make_column("_id", &PicitEntity::_id, autoincrement(), primary_key()),
                                        make_column("vpath", &PicitEntity::vpath),
                                        make_column("abspath", &PicitEntity::abspath),
                                        make_column("version", &PicitEntity::version),
                                        make_column("sha1", &PicitEntity::sha1),
                                        make_column("removed", &PicitEntity::removed),
                                        make_column("create_time", &PicitEntity::create_time),
                                        make_column("update_time", &PicitEntity::update_time))
                );
        return storage;
    }

public:

    bool is_latest(const PicitEntity &entity) {
        auto latest = repo().max(&PicitEntity::version, where(c(&PicitEntity::vpath) == entity.vpath));
        if (latest) {
            return entity.version == *latest;
        } else {
            return true;
        }
    }

    Optional<PicitEntity> get_by_sha1(const std::string &sha1) {
        auto pics = repo().get_all<PicitEntity>(where(c(&PicitEntity::sha1) == sha1));
        if (!pics.empty()) {
            return Optional<PicitEntity>(pics[0]);
        } else {
            return {};
        }
    }

    Optional<PicitEntity> get_by_vpath(const std::string &vpath) {
        auto pics = repo().get_all<PicitEntity>(where(c(&PicitEntity::vpath) == vpath));
        if (!pics.empty()) {
            return Optional<PicitEntity>(*std::max_element(pics.begin(), pics.end()));
        } else {
            return {};
        }
    }

    int save_file(const std::string &vpath, const std::string &abspath, int version, const std::string &sha1) {
        try {
            PicitEntity entity;
            entity.vpath = vpath;
            entity.abspath = abspath;
            entity.version = version;
            entity.sha1 = sha1;
            return repo().insert(entity);
        } catch (std::exception &e) {
            LOGGER->error("Error occured in SQLite: {}", e.what());
            return -1;
        } catch (...) {
            LOGGER->error("Error occured in SQLite: Unknown C language");
            return -1;
        }

    }
};

}
}
}

